#include "SimpleTrader.mqh"


#define D_DEBUG_PRINT false

SimpleTrader::SimpleTrader()
{
}

SimpleTrader::~SimpleTrader()
{

}

SimpleTrader::Init(string symbol, int magicCode, double losts = 0.1)
{
	this.symbol    = symbol;
	this.MagicCode = magicCode;
	this.losts     = losts;
	
	InpTakeProfit   =50;  // Take Profit (in pips)
	InpTrailingStop =30;  // Trailing Stop Level (in pips)
	m_symbol.Name(symbol);


	m_trade.SetExpertMagicNumber(MagicCode); // magic
	m_trade.SetMarginMode();
	m_trade.SetTypeFillingBySymbol(symbol);
	//--- tuning for 3 or 5 digits
	int digits_adjust=1;
	if(m_symbol.Digits()==3 || m_symbol.Digits()==5)
		digits_adjust=10;
    m_adjusted_point=m_symbol.Point()*digits_adjust;
    m_traling_stop    =InpTrailingStop*m_adjusted_point;
    m_take_profit = InpTakeProfit*m_adjusted_point;
	//--- set default deviation for trading in adjusted points
	m_trade.SetDeviationInPoints(3*digits_adjust);

	m_position.Select(symbol);

}

SimpleTrader::Buy()
{
	Update();
	ulong req_order = m_trade.RequestOrder();
	ENUM_TRADE_REQUEST_ACTIONS req_act = m_trade.RequestAction();
	ENUM_ORDER_TYPE  curr_order_type = m_trade.RequestType();
	if(req_act == 0)
	   PositionOpenBuy();
	else if(curr_order_type==ORDER_TYPE_BUY)
		return;
	else if(curr_order_type==ORDER_TYPE_SELL)
	{
		PositionClose();
	   PositionOpenBuy();
	}
	else
	   printf("SimpleTrader::Buy status error!");
   
}

SimpleTrader::Sell()
{
	Update();
	
	ulong req_order = m_trade.RequestOrder();
	ENUM_TRADE_REQUEST_ACTIONS req_act = m_trade.RequestAction();
	ENUM_ORDER_TYPE  curr_order_type = m_trade.RequestType();
	if(req_act == 0)
	   	PositionOpenSell();
	else if(curr_order_type == ORDER_TYPE_SELL)
	    return;
	else if(curr_order_type == ORDER_TYPE_BUY)
	{
	    PositionClose();
	    PositionOpenSell();
	}
	else
	   printf("SimpleTrader::Sell status error!");
   	    
}

SimpleTrader::Close()
{
	Update();
	ulong req_order = m_trade.RequestOrder();
	ENUM_TRADE_REQUEST_ACTIONS req_act = m_trade.RequestAction();
	ENUM_ORDER_TYPE  curr_order_type = m_trade.RequestType();

   if(req_act == 0)
      return;
	if(curr_order_type==ORDER_TYPE_BUY ||
		curr_order_type == ORDER_TYPE_SELL)
		PositionClose();
}

SimpleTrader::PositionOpenBuy()
{
	double price = m_symbol.Ask();
	// double tp    = m_symbol.Bid()+m_take_profit;
   double tp    = 0;

	//--- check for free money
	if(m_account.FreeMarginCheck(symbol,ORDER_TYPE_BUY,losts,price)<0.0)
	   printf("We have no money. Free Margin = %f",m_account.FreeMargin());
	else
	{
	//--- open position
		if(m_trade.PositionOpen(symbol,ORDER_TYPE_BUY,losts,price,0.0,tp))
			printf("Position by %s to be opened",symbol);
		else
		{
			printf("Error opening BUY position by %s : '%s'",symbol,m_trade.ResultComment());
			printf("Open parameters : price=%f,TP=%f",price,tp);
		}
	}

}

SimpleTrader::PositionOpenSell()
{
    double price=m_symbol.Bid();
	 // double tp    = m_symbol.Bid()+m_take_profit;
    double tp    = 0;
    //--- check for free money
    if(m_account.FreeMarginCheck(symbol,ORDER_TYPE_SELL,losts,price)<0.0)
       printf("We have no money. Free Margin = %f",m_account.FreeMargin());
    else
      {
       //--- open position
       if(m_trade.PositionOpen(symbol,ORDER_TYPE_SELL,losts,price,0.0,tp))
          printf("Position by %s to be opened",symbol);
       else
         {
          printf("Error opening SELL position by %s : '%s'",symbol,m_trade.ResultComment());
          printf("Open parameters : price=%f,TP=%f",price,tp);
         }
      }

}

SimpleTrader::PositionClose()
{
	if(m_trade.PositionClose(symbol))
	{
		printf("PositionClose: %s", symbol);
	}
	else
	{
		printf("Error closing position by %s : '%s'",symbol,m_trade.ResultComment());
	}
}

bool SimpleTrader::Update()
{
	if(!m_symbol.RefreshRates())
		printf("Error refresh rates faild");
		return(false);
	return true;
}



//==============================

int CheckLoadHistory(string symbol,ENUM_TIMEFRAMES period,datetime start_date) 
  { 
   datetime first_date=0; 
   datetime times[100]; 
//--- check symbol & period 
   if(symbol==NULL || symbol=="") symbol=Symbol(); 
   if(period==PERIOD_CURRENT)     period=Period(); 
//--- check if symbol is selected in the Market Watch 
   if(!SymbolInfoInteger(symbol,SYMBOL_SELECT)) 
     { 
      if(GetLastError()==ERR_MARKET_UNKNOWN_SYMBOL) return(-1); 
      SymbolSelect(symbol,true); 
     } 
//--- check if data is present 
   SeriesInfoInteger(symbol,period,SERIES_FIRSTDATE,first_date); 
   if(first_date>0 && first_date<=start_date) return(1); 
//--- don't ask for load of its own data if it is an indicator 
   if(MQL5InfoInteger(MQL5_PROGRAM_TYPE)==PROGRAM_INDICATOR && Period()==period && Symbol()==symbol) 
      return(-4); 
//--- second attempt 
   if(SeriesInfoInteger(symbol,PERIOD_M1,SERIES_TERMINAL_FIRSTDATE,first_date)) 
     { 
      //--- there is loaded data to build timeseries 
      if(first_date>0) 
        { 
         //--- force timeseries build 
         CopyTime(symbol,period,first_date+PeriodSeconds(period),1,times); 
         //--- check date 
         if(SeriesInfoInteger(symbol,period,SERIES_FIRSTDATE,first_date)) 
            if(first_date>0 && first_date<=start_date) return(2); 
        } 
     } 
//--- max bars in chart from terminal options 
   int max_bars=TerminalInfoInteger(TERMINAL_MAXBARS); 
//--- load symbol history info 
   datetime first_server_date=0; 
   while(!SeriesInfoInteger(symbol,PERIOD_M1,SERIES_SERVER_FIRSTDATE,first_server_date) && !IsStopped()) 
      Sleep(5); 
//--- fix start date for loading 
   if(first_server_date>start_date) start_date=first_server_date; 
   if(first_date>0 && first_date<first_server_date) 
      Print("Warning: first server date ",first_server_date," for ",symbol, 
            " does not match to first series date ",first_date); 
//--- load data step by step 
   Print("load data from ", first_date, " to ", start_date);
   int fail_cnt=0; 
   while(!IsStopped()) 
     { 
      //--- wait for timeseries build 
      while(!SeriesInfoInteger(symbol,period,SERIES_SYNCHRONIZED) && !IsStopped()) 
         Sleep(5); 
      //--- ask for built bars 
      int bars=Bars(symbol,period); 
      if(bars>0) 
        { 
         if(bars>=max_bars) return(-2); 
         //--- ask for first date 
         if(SeriesInfoInteger(symbol,period,SERIES_FIRSTDATE,first_date)) 
            if(first_date>0 && first_date<=start_date) return(0); 
        } 
      //--- copying of next part forces data loading 
      int copied=CopyTime(symbol,period,bars,100,times); 
      if(copied>0) 
        { 
         Print("load data at  ", times[0], " / ", start_date);
         //--- check for data 
         if(times[0]<=start_date)  return(0); 
         if(bars+copied>=max_bars) return(-2); 
         fail_cnt=0; 
        } 
      else 
        { 
         //--- no more than 100 failed attempts 
         fail_cnt++; 
         if(fail_cnt>=100) return(-5); 
         Sleep(10); 
        } 
     } 
//--- stopped 
   return(-3); 
  } 
  
/**
 * 
 */
RateDataCollector::RateDataCollector()
{
}


RateDataCollector::~RateDataCollector()
{


}

void RateDataCollector::Init(string symbol, ENUM_TIMEFRAMES  timeframe)
{
	m_symbolName = symbol;
	m_timeFrame = timeframe;
	m_skipNum = 1024;
	m_symbol.Name(m_symbolName);

}

void RateDataCollector::GetRateData(int count, MqlRates &rates[], int &count_out)
{
    ArraySetAsSeries(rates, false);
    count_out = CopyRates(
                        m_symbolName,
                        m_timeFrame,
                        1,
                        count,
                        rates );
                        
    if(D_DEBUG_PRINT)
    {
      printf("\n Get rate: %d", count_out);
    	for(int i=0;i<count_out;i++)
    	{
    		printf("[%5d] time=%s, open=%.4f, high=%.4f, low=%.4f, close=%.4f, tick_vol=%ld, spread=%3d, real_vol=%ld", 
    		   i, 
    		   TimeToString(rates[i].time),
    		   rates[i].open,
    		   rates[i].high,
    		   rates[i].low,
    		   rates[i].close,
    		   rates[i].tick_volume,
    		   rates[i].spread,
    		   rates[i].real_volume
    		  );
    	}
    	
    	printf("\n\n");
    }

    return;           
}


void RateDataCollector::GetByTime(MqlRates &rates[], int &count_out, datetime start, datetime end)
{
   datetime dateTmp=0;
   bool isSync;
   Print("GetByTime: Symbol=", m_symbolName, " TimeFrame=", m_timeFrame, "From ", start, " To ", end);
   SeriesInfoInteger(m_symbolName,m_timeFrame,SERIES_FIRSTDATE,dateTmp);
   Print("SERIES_FIRSTDATE: ", dateTmp);
   SeriesInfoInteger(m_symbolName,m_timeFrame,SERIES_LASTBAR_DATE,dateTmp);
   Print("SERIES_LASTBAR_DATE: ", dateTmp);
   SeriesInfoInteger(m_symbolName,m_timeFrame,SERIES_SERVER_FIRSTDATE,dateTmp);
   Print("SERIES_SERVER_FIRSTDATE: ", dateTmp);      
   SeriesInfoInteger(m_symbolName,m_timeFrame,SERIES_TERMINAL_FIRSTDATE,dateTmp);
   Print("SERIES_TERMINAL_FIRSTDATE: ", dateTmp);
   isSync = SeriesInfoInteger(m_symbolName,m_timeFrame,SERIES_SYNCHRONIZED);
   Print("SERIES_SYNCHRONIZED: ", isSync);
   
   int res = CheckLoadHistory(m_symbolName, m_timeFrame, start);
 	switch(res) 
  { 
   case -1 : Print("Unknown symbol ",m_symbolName);             break; 
   case -2 : Print("Requested bars more than max bars in chart"); break; 
   case -3 : Print("Program was stopped");                        break; 
   case -4 : Print("Indicator shouldn't load its own data");      break; 
   case -5 : Print("Load failed");                                break; 
   case  0 : Print("Loaded OK");                                  break; 
   case  1 : Print("Loaded previously");                          break; 
   case  2 : Print("Loaded previously and built");                break; 
   default : Print("Unknown result"); 
  } 
    
    ArraySetAsSeries(rates, false);
    count_out = CopyRates(
                        m_symbolName,
                        m_timeFrame,
                        start,
                        end,
                        rates );                       
                        
   printf("Get count: %d", count_out);
    return;           
}




// =======================
// RemoteTrader
// =======================
RemoteTrader::RemoteTrader()
{

}

RemoteTrader::~RemoteTrader()
{

}

void RemoteTrader::Init(string symbol, ENUM_TIMEFRAMES timeframe, SimpleTrader &trader)
{
	_symbol = symbol;
	_timeframe = timeframe;
	_trader = trader;
	_collector.Init(symbol, timeframe);
	_pipe.Init("127.0.0.1", 9000);
	_getActionReq.Init(_pipe);
	_last_check_time = 0;

	_update_interve = 60 * _timeframe;
	_last_itme_time=0;

}
void RemoteTrader::Process()
{	
	int skipNum = 1024;
	MqlRates rates[];
	int rate_count;
	datetime curr_time;


	if(CheckUpdate() == false)
		return;

	int rateCount = skipNum + 1;
	_collector.GetRateData(rateCount, rates, rate_count);
	curr_time = rates[rate_count-1].time;
	
	if(curr_time < _last_itme_time + _update_interve)
		return;
	
	_last_itme_time = curr_time;

	_getActionReq.GetAction(rates, rateCount);

	OrderType orderType = _getActionReq.OrderArr[rateCount-1];
   Print("[", curr_time, "}", "Order=", orderType);
	if(orderType == OrderType_Buy)
	{
		_trader.Buy();
	}
	else if(orderType == OrderType_Sell)
	{
		_trader.Sell();
	}

	return;
}

void RemoteTrader::Close()
{
	_pipe.Close();
}

bool RemoteTrader::CheckUpdate()
{
	return true;
}

